﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using BingleMaps.Models.GoogleMaps;
using BingleMaps.ViewModels;
using Newtonsoft.Json;
using RestSharp;

namespace BingleMaps.Services
{
	public static class GoogleMapsServices
	{
		private const string UnitImperial = "imperial";
		private const string UnitMetric = "metric";
		private const string BaseApiUrl = "https://maps.googleapis.com/maps/api";
		private const string BaseStaticMapUrl = "staticmap?sensor=true";
		private const string ApiKey = "AIzaSyBEPZ46IhE3WI-7mBtSOkixMr6GSwRgrws";
		private static readonly string _currentCaltureName = CultureInfo.CurrentCulture.Name;
		private static readonly string _currentRegionCode = RegionInfo.CurrentRegion.TwoLetterISORegionName;

		private static readonly string _basePlacesSearchUrl = "place/search/json?sensor=true&radius=500&key=" + ApiKey + "&language=" + _currentCaltureName;
		private static readonly string _basePlaceDetailsUrl = "place/details/json?sensor=true&key=" + ApiKey + "&language=" + _currentCaltureName;
		private static readonly string _baseGeocodingUrl = "geocode/json?sensor=true&language=" + _currentCaltureName + "&region=" + _currentRegionCode;
		private static readonly string _baseDirectionsUrl = "directions/sensor=true&language=" + _currentCaltureName + "&region=" + _currentRegionCode;
		private static readonly string _baseAutoCompleteUrl = "place/autocomplete/json?sensor=true&key=" + ApiKey + "&language=" + _currentCaltureName;

		public static void GetDirections(string origion, string destination, TravelMode mode, SettingsViewModel.UnitKey unit, Action<Route> callback)
		{
			var c = new RestClient(BaseApiUrl);
			string strUnit;
			switch (unit)
			{
				case SettingsViewModel.UnitKey.Imperial:
					strUnit = UnitImperial;
					break;
				case SettingsViewModel.UnitKey.Metric:
					strUnit = UnitMetric;
					break;
				default:
					strUnit = RegionInfo.CurrentRegion.IsMetric ? UnitMetric : UnitImperial;
					break;
			}
			c.ExecuteAsync(new RestRequest(_baseDirectionsUrl + "&origion=" + origion + "&destination=" + destination + "&mode=" + mode + "&units=" + strUnit),
						   (handle, restRsp) =>
						   {
							   if (restRsp == null || restRsp.StatusCode != HttpStatusCode.OK)
							   {
								   callback(null);
							   }
							   else
							   {
								   EnumerableResponse<Route> directionsRsp;
								   try
								   {
									   directionsRsp = JsonConvert.DeserializeObject<EnumerableResponse<Route>>(restRsp.Content);
								   }
								   catch
								   {
									   callback(null);
									   return;
								   }
								   if (directionsRsp == null)
								   {
									   callback(null);
									   return;
								   }
								   callback(directionsRsp.Results.FirstOrDefault());
							   }
						   });
		}

		public static string GetStaticMap(Location center, int zoom, Size size)
		{
			return BaseApiUrl + "/" + BaseStaticMapUrl + "&center=" + center + "&zoom=" + zoom + "&size=" + size;
		}

		public static void SearchPlaces(Location location, string query, int pageSize, Action<IEnumerable<Place>> callback)
		{
			var c = new RestClient(BaseApiUrl);
			c.ExecuteAsync(new RestRequest(_basePlacesSearchUrl + "&location=" + location + (query == null ? null : "&name=" + HttpUtility.UrlEncode(query))),
						   (handle, restRsp) =>
						   {
							   if (restRsp == null || restRsp.StatusCode != HttpStatusCode.OK)
							   {
								   callback(null);
							   }
							   else
							   {
								   EnumerableResponse<Place> rsp;
								   try
								   {
									   rsp = JsonConvert.DeserializeObject<EnumerableResponse<Place>>(restRsp.Content);
								   }
								   catch
								   {
									   callback(null);
									   return;
								   }
								   if (rsp == null)
								   {
									   callback(null);
									   return;
								   }
								   callback(pageSize >= rsp.Results.Count() ? rsp.Results : rsp.Results.Take(pageSize));
							   }
						   });
		}

		public static void Geocode(string address, ViewPort bounds, Action<IEnumerable<PlaceDetails>> callback)
		{
			var c = new RestClient(BaseApiUrl);
			c.ExecuteAsync(new RestRequest(_baseGeocodingUrl + "&address=" + HttpUtility.UrlEncode(address) + "&bounds=" + bounds),
						   (handle, restRsp) =>
						   {
							   if (restRsp == null || restRsp.StatusCode != HttpStatusCode.OK)
							   {
								   callback(null);
							   }
							   else
							   {
								   EnumerableResponse<PlaceDetails> mapRsp;
								   try
								   {
									   mapRsp = JsonConvert.DeserializeObject<EnumerableResponse<PlaceDetails>>(restRsp.Content);
								   }
								   catch
								   {
									   callback(null);
									   return;
								   }
								   if (mapRsp == null)
								   {
									   callback(null);
									   return;
								   }
								   callback(mapRsp.Results);
							   }
						   });
		}

		public static void ReverseGeocode(Location location, Action<IEnumerable<PlaceDetails>> callback)
		{
			var c = new RestClient(BaseApiUrl);
			c.ExecuteAsync(new RestRequest(_baseGeocodingUrl + "&latlng=" + location),
						   (handle, restRsp) =>
						   {
							   if (restRsp == null || restRsp.StatusCode != HttpStatusCode.OK)
							   {
								   callback(null);
							   }
							   else
							   {
								   EnumerableResponse<PlaceDetails> mapRsp;
								   try
								   {
									   mapRsp = JsonConvert.DeserializeObject<EnumerableResponse<PlaceDetails>>(restRsp.Content);
								   }
								   catch
								   {
									   callback(null);
									   return;
								   }
								   if (mapRsp == null)
								   {
									   callback(null);
									   return;
								   }
								   callback(mapRsp.Results);
							   }
						   });
		}

		public static void GetPredictions(Location location, string input, Action<IEnumerable<Suggestion>> callback)
		{
			var c = new RestClient(BaseApiUrl);
			c.ExecuteAsync(new RestRequest(_baseAutoCompleteUrl + "&location=" + location + "&input=" + input),
						   (handle, restRsp) =>
						   {
							   if (restRsp == null || restRsp.StatusCode != HttpStatusCode.OK)
							   {
								   callback(null);
							   }
							   else
							   {
								   AutoCompleteResponse rsp;
								   try
								   {
									   rsp = JsonConvert.DeserializeObject<AutoCompleteResponse>(restRsp.Content);
								   }
								   catch
								   {
									   callback(null);
									   return;
								   }
								   callback(rsp.Suggestions);
							   }
						   });
		}

		public static void GetPlaceDetails(string reference, Action<PlaceDetails> callback)
		{
			var c = new RestClient(BaseApiUrl);
			c.ExecuteAsync(new RestRequest(_basePlaceDetailsUrl + "&reference=" + reference),
						   (handle, restRsp) =>
						   {
							   if (restRsp == null || restRsp.StatusCode != HttpStatusCode.OK)
							   {
								   callback(null);
							   }
							   else
							   {
								   Response<PlaceDetails> rsp;
								   try
								   {
									   rsp = JsonConvert.DeserializeObject<Response<PlaceDetails>>(restRsp.Content);
								   }
								   catch
								   {
									   callback(null);
									   return;
								   }
								   callback(rsp.Result);
							   }
						   });
		}
	}
}
